#include "lv_porting.h"


//#define DEBUG
//#define DEBUG2

Epd epd;
static uint8_t img_buf[DISP_HOR_RES*DISP_VER_RES];
static Paint paint(img_buf,DISP_HOR_RES,DISP_VER_RES);

static void epd_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
  uint32_t w = (area->x2 - area->x1 + 1);
	uint32_t h = (area->y2 - area->y1 + 1);

    for(int i = area->y1; i <= area->y2; i ++){
        for(int j = area->x1; j <= area->x2; j ++){
            if(color_p[(j-area->x1) + (i-area->y1)*w].full == 0xffff) paint.DrawPixel(j, i, UNCOLORED);
            else paint.DrawPixel(j, i, COLORED);
            #ifdef DEBUG
              Serial.printf("%d\n",color_p[(j-area->x1) + (i-area->y1)*w].full);
            #endif // DEBUG               
        }
    }
    #ifdef DEBUG
      Serial.println("lvgl flush complete");
    #endif // DEBUG
    
    lv_disp_flush_ready(disp_drv);
}

int8_t lv_hal_init()
{

  /*-----------------------------
   * LVGL init
   *----------------------------*/
  lv_init();

  /*-----------------------------
   * EPD driver init
   *----------------------------*/
  if (epd.Init(lut_full_update) != 0) {
    Serial.print("e-Paper init failed");
    return -1;
  }

  #ifdef DEBUG2
    Serial.println("e-Paper init compete phase 1");
  #endif // DEBUG

  epd.ClearFrameMemory(0xFF);
  epd.DisplayFrame(); // bit set = white, bit reset = black

  //epd.Sleep();

  #ifdef DEBUG2
    Serial.println("e-Paper sleep");
  #endif // DEBUG

  if (epd.Init(lut_partial_update) != 0) {
    Serial.print("e-Paper init failed");
    return -1;
  }

  #ifdef DEBUG2
    Serial.println("e-Paper init compete phase 2");
  #endif // DEBUG

  paint.SetRotate(ROTATE_270);
  paint.SetHeight(EPD_HEIGHT);
  paint.SetWidth(EPD_WIDTH);
  paint.Clear(UNCOLORED);

  /*-----------------------------
   * Create a buffer for drawing
   *----------------------------*/
  static lv_disp_draw_buf_t disp_buf1;
  static lv_color_t buf_1_1[DISP_HOR_RES*10];
  lv_disp_draw_buf_init(&disp_buf1,buf_1_1,NULL,DISP_HOR_RES*10);

  /*-----------------------------------
   * Register the display in LVGL
   *----------------------------------*/
  static lv_disp_drv_t disp_drv;
  lv_disp_drv_init(&disp_drv);

  /*Set the resolution of the display*/
  disp_drv.hor_res=DISP_HOR_RES;
  disp_drv.ver_res=DISP_VER_RES;

  /*Used to copy the buffer's content to the display*/
  disp_drv.flush_cb=epd_flush_cb;

  /*Set a display buffer*/
  disp_drv.draw_buf=&disp_buf1;

  /*Finally register the driver*/
  lv_disp_drv_register(&disp_drv);

  delay(200);

  #ifdef DEBUG
    Serial.println("lvgl init complete");
  #endif // DEBUG
  
  return 0;

}


LV_DISPLAY::LV_DISPLAY(int8_t scl,int8_t sda,int8_t rst,int8_t dc,int8_t cs,int8_t busy)
{
    _scl=scl;
    _sda=sda;
    _rst=rst;
    _dc=dc;
    _cs=cs;
    _busy=busy;
}

int8_t LV_DISPLAY::begin(){
  return lv_hal_init();
}

void LV_DISPLAY::refresh(uint8_t Mode)
{
  if(Mode==FULL)
  {
    epd.Init(lut_full_update);
  }
  else if(Mode==PARTICAL)
  {
    epd.Init(lut_partial_update);
  }
  epd.SetFrameMemory(paint.GetImage());
	epd.DisplayFrame();
  #ifdef DEBUG2
    Serial.println("refresh complete");
  #endif // DEBUG
  
  epd.Sleep();
}

LV_DISPLAY EPD;